﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;

using vJine.Core.IoC;

namespace vJine.Core {
    public partial class AppContext {
        [Serializable]
        public class Module {
            [XmlAttribute]
            public string Name { get; set; }
            [XmlAttribute]
            public string Version { get; set; }
            [XmlAttribute]
            public string Author { get; set; }
            [XmlAttribute]
            public string Company { get; set; }
            [XmlAttribute]
            public string CopyRight { get; set; }
            [XmlElement]
            public string SupportURL { get; set; }

            [XmlAttribute]
            public bool? IsService { get; set; }
            [XmlAttribute]
            public bool? OnServer { get; set; }
            [XmlAttribute]
            public bool? OnClient { get; set; }

            [XmlElement]
            public string Signature { get; set; }
            [XmlElement]
            public string Path { get; set; }

            internal bool IsLoaded { get; set; }
            public Assembly Assembly { get; set; }

            bool IsSaved { get; set; }
            private byte[] _Bin = null;
            public byte[] Bin {
                get {
                    return this._Bin;
                }
                set {
                    if (value != this._Bin) {
                        this._Bin = value;
                        this.IsSaved = false;
                    }
                }
            }

            public void Save() {
                this.Save(Environment.CurrentDirectory);
            }

            public void Save(string BaseDir) {
                if (this.Bin == null) {
                    return;
                }

                if (!this.IsSaved) {
                    string FileName = System.IO.Path.Combine(BaseDir, this.Path);
                    string FileDir = System.IO.Path.GetDirectoryName(FileName);
                    if (!Directory.Exists(FileDir)) {
                        Directory.CreateDirectory(FileDir);
                    }
                    File.WriteAllBytes(FileName, this.Bin);

                    this.IsSaved = true;
                }
            }

            public void Load() {
                this.Load(Environment.CurrentDirectory);
            }

            public void Load(string BaseDir) {
                if (this.Bin == null) {
                    string FileName = System.IO.Path.Combine(BaseDir, this.Path);
                    this.Bin = File.ReadAllBytes(FileName);
                }
            }

            public void Load(AppDomain Domain) {
                this.Load();

                if (this.Assembly == null) {
                    this.Assembly = this.GetAssemblyByName(this.Name);
                    if (this.Assembly == null) {
                        this.Assembly = Domain.Load(this.Bin);
                    }
                }
            }

            public void Load(AppDomain Domain, string BaseDir) {
                this.Load(BaseDir);
                if (this.Assembly == null) {
                    this.Assembly = Domain.Load(this.Bin);
                }
            }

            public Assembly GetAssemblyByName(string Name) {
                if (Name.IndexOf(",") < 0) {
                    Name += ",";
                }

                Assembly[] As = AppDomain.CurrentDomain.GetAssemblies();
                for (int i = 0; i < As.Length; i++) {
                    if (As[i].FullName.StartsWith(Name)) {
                        return As[i];
                    }
                }

                return null;
            }
        }

        [Serializable]
        public class ModuleCollection : List<Module> {
            [XmlAttribute]
            public string Version { get; set; }

            public Module GetModuleByName(string Name) {
                for (int i = 0; i < this.Count; i++) {
                    if (Name == this[i].Name) {
                        return this[i];
                    }
                }

                return null;
            }
        }

        [Serializable]
        public class Sequence {
            [XmlAttribute]
            public string Name { get; set; }
            [XmlAttribute]
            public string Version { get; set; }

            [XmlArray]
            public InstanceCollection Intances { get; set; }
        }

        [Serializable]
        public class SequenceCollection : List<Sequence> {
            [XmlAttribute]
            public string Default { get; set; }

            [XmlAttribute]
            public string Version { get; set; }

            public Sequence Active {
                get {
                    if (this.Count == 0) {
                        throw new CoreException("无法获取ActiveSequence");
                    }
                    if (string.IsNullOrEmpty(this.Default)) {
                        return this[0];
                    }

                    Sequence active = this.GetByName(this.Default);
                    if (active == null) {
                        throw new CoreException("无法获取ActiveSequence");
                    } else {
                        return active;
                    }
                }
            }

            public Sequence GetByName(string Name) {
                for (int i = 0, len = this.Count; i < len; i++) {
                    if (this[i].Name == Name) {
                        return this[i];
                    }
                }

                return null;
            }
        }

        [Serializable]
        public class Instance {
            public Instance() {
                this.Single = true;
            }

            [XmlAttribute]
            public string Name { get; set; }
            [XmlAttribute]
            public bool Single { get; set; }
            [XmlElement]
            public type Class { get; set; }
            [XmlElement]
            public ParamCollection Params { get; set; }

            [XmlElement]
            public Invoker Invoke { get; set; }

            public object Context { get; set; }

            public object Create(InstanceCollection Instances) {
                if (string.IsNullOrEmpty(this.Name)) {
                    return this.Create();
                } else if (!this.Name.StartsWith("#")) {
                    return this.Create();
                } else {
                    return Instances.GetContextByName(this.Name.Substring(1));
                }
            }

            public object Create() {
                if (this.Single && this.Context != null) {
                    return this.Context;
                }

                bool HasParams = this.Params != null;
                this.Context =
                    vJine.Core.IoC.Class.init(this.Class.Name,
                        HasParams ? this.Params.GetTypes() : null, HasParams ? this.Params.GetValues() : null);

                return this.Context;
            }

            bool IsCreated = false;

            public object Execute(InstanceCollection Instances) {
                return this.Execute(null, Instances);
            }

            public object Execute(object Context, InstanceCollection Instances) {
                if (!this.IsCreated) {
                    this.Create(Instances);
                }

                if (this.Invoke == null) {
                    return this.Context;
                }
                object exeContext = Context == null ? this.Context : Context;

                switch (this.Invoke.Cmd) {
                    case Invoker.Command.Set:
                        return vJine.Core.IoC.Class.
                            Set(this.Class.Name, this.Invoke.Name).
                            Invoke(exeContext,
                                this.Invoke.Value != null ?
                                this.Invoke.Value.Execute((InstanceCollection)null) :
                                vJine.Core.IoC.Class.Parse(this.Invoke.value, Reflect.GetType(this.Invoke.Class.Name)));
                    case Invoker.Command.Get:
                        return vJine.Core.IoC.Class.
                            Get(this.Class.Name, this.Invoke.Name).
                            Invoke(exeContext);
                    case Invoker.Command.Call:
                        return vJine.Core.IoC.Class.
                            Call(this.Class.Name, this.Invoke.Name, this.Invoke.Params == null ? null : this.Invoke.Params.GetTypes()).
                            Invoke(exeContext, this.Invoke.Params == null ? null : this.Invoke.Params.GetValues());
                    default:
                        throw new CoreException("未知的Invoke命令【{0}】", this.Invoke.ToString());
                }
            }
        }

        [Serializable]
        public class InstanceCollection : List<Instance> {
            public object GetContextByName(string Name) {
                for (int i = 0; i < this.Count; i++) {
                    if (Name == this[i].Name) {
                        return this[i].Create();
                    }
                }

                return null;
            }
        }

        [Serializable]
        public class Invoker {
            public enum Command : byte {
                Unknown = 0,
                Set = 1,
                Get = 2,
                Call = 3
            }

            [XmlAttribute]
            public Command Cmd { get; set; }

            [XmlAttribute]
            public string Name { get; set; }

            [XmlElement]
            public type Class { get; set; }
            [XmlElement]
            public string value { get; set; }
            [XmlElement]
            public Instance Value { get; set; }

            [XmlArray]
            public ParamCollection Params { get; set; }

            [XmlAttribute]
            public string Return { get; set; } //返回值回设
        }

        [Serializable]
        public class Param {
            [XmlAttribute]
            public string Name { get; set; }
            [XmlElement]
            public type Class { get; set; }
            [XmlElement]
            public string value { get; set; }
            [XmlElement]
            public Instance Value { get; set; }
        }

        [Serializable]
        public class ParamCollection : List<Param> {
            public string[] GetTypes() {
                string[] T = new string[this.Count];
                for (int i = 0; i < this.Count; i++) {
                    T[i] = this[i].Class.Name;
                }

                return T;
            }

            public object[] GetValues() {
                object[] V = new object[this.Count];
                for (int i = 0; i < this.Count; i++) {
                    V[i] =
                        this[i].Value != null ?
                        this[i].Value.Execute((InstanceCollection)null) : Class.Parse(this[i].value, Reflect.GetType(this[i].Class.Name));
                }

                return V;
            }
        }

        [Serializable]
        public class type {
            public static implicit operator string(type T) {
                return T.Name;
            }

            public static implicit operator type(string t) {
                Type T = Reflect.GetType(t);
                return new type() {
                    Name = Reflect.GetTypeName(T)
                };
            }

            [XmlAttribute]
            public string Name { get; set; }
            [XmlArray]
            public List<string> Generics { get; set; }

            public List<Invoker> Methods { get; set; }
        }
    }
}
